Add dual CJS/ESM build#326
Open
tomassabol wants to merge 2 commits into
Open
Conversation
Collaborator
|
Hey @tomassabol, Can you please resolve the CI errors? 🙏 |
There was a problem hiding this comment.
Pull request overview
This PR introduces a dual-build distribution strategy for lambda-api, adding first-class ESM output while preserving CommonJS compatibility via conditional exports and SWC-built artifacts in dist/cjs and dist/esm.
Changes:
- Migrate source to
src/and compile todist/cjs+dist/esmusing SWC. - Add an
exportsmap to supportrequire()/import(including deep./lib/*subpaths). - Add module-compatibility tests validating CJS + ESM loading and basic runtime behavior.
Reviewed changes
Copilot reviewed 20 out of 23 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/package.json | Marks src scope as ESM (type: module) to drive ESM compilation behavior. |
| src/index.js | Converts entrypoint to ESM syntax and adds CJS fallback export logic. |
| src/lib/utils.js | Converts utilities to ESM exports/imports and removes CJS exports.* usage. |
| src/lib/statusCodes.js | Switches to ESM default export with CJS fallback assignment. |
| src/lib/s3-service.js | Adds new ESM S3 service implementation intended for both builds. |
| src/lib/response.js | Converts response module to ESM and updates S3 usage sites. |
| src/lib/request.js | Converts request module to ESM default export with CJS fallback. |
| src/lib/prettyPrint.js | Switches to ESM default export with CJS fallback assignment. |
| src/lib/mimemap.js | Switches to ESM default export with CJS fallback assignment. |
| src/lib/logger.js | Converts logger module exports to ESM named exports. |
| src/lib/errors.js | Converts error exports to ESM named exports. |
| src/lib/compression.js | Converts compression module to ESM named export(s). |
| package.json | Adds SWC build scripts, exports map, and publishes dist/ output. |
| lib/s3-service.js | Removes legacy root lib/ S3 service (source now under src/). |
| jest.config.js | Adds Jest mapping to test against built dist/cjs artifacts. |
| index.test-d.ts | Updates type tests to validate default factory typing alignment. |
| .swcrc.esm.json | Adds SWC config for ESM output build. |
| .swcrc.cjs.json | Adds SWC config for CJS output build. |
| .gitignore | Ignores generated dist/ output. |
| .eslintrc.json | Adjusts ESLint parsing defaults with ESM overrides for src/**/*.js and __tests__/**/*.mjs. |
| tests/module-compat.unit.js | Adds CJS/exports-resolution compatibility tests. |
| tests/esm-compat.mjs | Adds Node ESM runtime compatibility smoke test for the ESM build. |
Comments suppressed due to low confidence (6)
src/index.js:14
- Importing the S3 helper at module load time makes the optional AWS SDK peerDependencies effectively required. Since src/lib/s3-service.js imports @aws-sdk/* at top-level, this static import can throw on startup for users who do not install the optional peers (even if they never use S3 features). Consider loading/configuring S3 lazily from response methods instead.
src/index.js:53 - This eager S3 client configuration will also break if the optional AWS SDK peerDependencies are not installed (and if S3 is no longer statically imported). Since S3 is only needed for getLink/sendFile with s3:// paths, defer S3 module loading/config until those helpers are invoked.
src/lib/response.js:13 - Static-importing ./s3-service.js causes the module to attempt loading optional @aws-sdk/* peerDependencies during package initialization, which can fail for users that don't install them. To preserve the current "optional S3" behavior, load the S3 service lazily only when getLink()/sendFile(s3://...) are used.
src/lib/response.js:200 - After switching to lazy-loading the S3 service, getLink() should resolve the S3 module before invoking getSignedUrl so the AWS SDK peer dependencies are only required when this method is called.
src/lib/response.js:342 - After switching to lazy-loading the S3 service, resolve/configure the S3 module before calling getObject() so users without the optional AWS SDK peer dependencies can still use non-S3 sendFile() paths.
src/index.js:570 - The CommonJS entrypoint previously set
module.exports.default = module.exportsto support consumers that access the factory via.default(and to align with the TypeScript default export). With the newmodule.exports = createAPIassignment, that compatibility property is dropped and can be a breaking change for existing CJS consumers.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds dual CommonJS and ESM support.
Until now, the package has only shipped as CommonJS, which can make modern ESM bundling workflows awkward. In particular, users bundling Lambda handlers to
.mjswith tools like esbuild can hit dynamicrequire()issues, as described in #295.This change keeps the existing CommonJS API working while adding a proper ESM build and package export map.
What Changed
src/and builds published output intodist/cjsanddist/esmwith SWCrequire()andimportlambda-api/lib/utilsWhy
This should make
lambda-apifriendlier for modern Lambda projects using ESM,.mjsentrypoints, or bundlers like esbuild, while remaining backwards compatible for existing CommonJS users.